package chen.huai.jie.spring.xmlconfig.mybatis;

import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.ResultType;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.reflection.TypeParameterResolver;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.springframework.core.MethodParameter;

import java.lang.reflect.*;
import java.util.*;

public class MapperUtils {
    private static Map<Class<?>, Map<Class<?>, EntityPortray>> portrayMap = new HashMap();

    public MapperUtils() {
    }

    public static EntityPortray getEntityPortray(Class<?> mapper, Class<?> type) {
        Map<Class<?>, EntityPortray> entities = (Map) portrayMap.computeIfAbsent(mapper, (clazz) -> {
            return new HashMap();
        });
        return (EntityPortray) entities.computeIfAbsent(type, EntityPortray::new);
    }

    public static void destroyPortray(Class<?> mapper) {
        portrayMap.remove(mapper);
    }

    public static Map<String, Class<?>> getParameters(Method method) {
        Map<String, Class<?>> parameterMap = new HashMap();
        Class<?>[] parameters = method.getParameterTypes();

        for (int i = 0; i < parameters.length; ++i) {
            MethodParameter parameter = new MethodParameter(method, i);
            Param param = (Param) parameter.getParameterAnnotation(Param.class);
            String key = parameters[i].getSimpleName();
            if (param != null) {
                key = param.value();
            }

            parameterMap.put(key, parameters[i]);
        }

        return parameterMap;
    }

    public static Class<?> getReturnType(Method method, Class<?> type) {
        Class<?> returnType = method.getReturnType();
        Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
        if (resolvedReturnType instanceof Class) {
            returnType = (Class) resolvedReturnType;
            if (Void.TYPE.equals(returnType)) {
                ResultType rt = (ResultType) method.getAnnotation(ResultType.class);
                if (rt != null) {
                    returnType = rt.value();
                }
            }
        } else if (resolvedReturnType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
            Class<?> rawType = (Class) parameterizedType.getRawType();
            Type[] actualTypeArguments;
            Type returnTypeParameter;
            if (!Collection.class.isAssignableFrom(rawType) && !Page.class.isAssignableFrom(rawType)) {
                if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
                    actualTypeArguments = parameterizedType.getActualTypeArguments();
                    if (actualTypeArguments != null && actualTypeArguments.length == 2) {
                        returnTypeParameter = actualTypeArguments[1];
                        if (returnTypeParameter instanceof Class) {
                            returnType = (Class) returnTypeParameter;
                        } else if (returnTypeParameter instanceof ParameterizedType) {
                            returnType = (Class) ((ParameterizedType) returnTypeParameter).getRawType();
                        }
                    }
                }
            } else {
                actualTypeArguments = parameterizedType.getActualTypeArguments();
                if (actualTypeArguments != null && actualTypeArguments.length == 1) {
                    returnTypeParameter = actualTypeArguments[0];
                    if (returnTypeParameter instanceof Class) {
                        returnType = (Class) returnTypeParameter;
                    } else if (returnTypeParameter instanceof ParameterizedType) {
                        returnType = (Class) ((ParameterizedType) returnTypeParameter).getRawType();
                    } else if (returnTypeParameter instanceof GenericArrayType) {
                        Class<?> componentType = (Class) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
                        returnType = Array.newInstance(componentType, 0).getClass();
                    }
                }
            }
        }

        return returnType;
    }

    public static SqlSource buildSqlSourceFromStrings(Configuration configuration, String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
        StringBuilder sql = new StringBuilder();
        String[] var5 = strings;
        int var6 = strings.length;

        for (int var7 = 0; var7 < var6; ++var7) {
            String fragment = var5[var7];
            sql.append(fragment);
            sql.append(" ");
        }

        return languageDriver.createSqlSource(configuration, prepareSql(sql.toString()), parameterTypeClass);
    }

    private static String prepareSql(String sql) {
        if (!sql.startsWith("select")) {
            return sql;
        } else {
            String sqlLower = sql.replaceAll(" from ", " FROM ");
            if (sqlLower.indexOf(" FROM ") == sqlLower.lastIndexOf(" FROM ")) {
                return sqlLower;
            } else {
                String subSql;
                for (Iterator var2 = splitSubSql(sqlLower).iterator(); var2.hasNext(); sqlLower = sqlLower.replace(subSql, subSql.replace(" FROM ", " from "))) {
                    subSql = (String) var2.next();
                }

                return sqlLower;
            }
        }
    }

    private static List<String> splitSubSql(String sql) {
        int startIndex = -1;
        int lastIndex = -1;
        int repeatCount = 0;
        List<String> subSqlList = new ArrayList();

        for (int i = 0; i < sql.length(); ++i) {
            if (sql.charAt(i) == '(') {
                if (startIndex == -1) {
                    startIndex = i;
                } else {
                    ++repeatCount;
                }
            } else if (sql.charAt(i) == ')') {
                if (repeatCount != 0) {
                    --repeatCount;
                } else {
                    lastIndex = i;
                }
            }

            if (startIndex != -1 && lastIndex != -1) {
                subSqlList.add(sql.substring(startIndex + 1, lastIndex));
                lastIndex = -1;
                startIndex = -1;
            }
        }

        return subSqlList;
    }

    public static Class<?> getParameterType(Method method) {
        Class<?> parameterType = null;
        Class<?>[] parameterTypes = method.getParameterTypes();
        Class[] var3 = parameterTypes;
        int var4 = parameterTypes.length;

        for (int var5 = 0; var5 < var4; ++var5) {
            Class<?> type = var3[var5];
            if (!PageRequest.class.isAssignableFrom(type) && !ResultHandler.class.isAssignableFrom(type)) {
                if (parameterType == null) {
                    parameterType = type;
                } else {
                    parameterType = MapperMethod.ParamMap.class;
                }
            }
        }

        return parameterType;
    }

    public static String buildTypeValue(String property, Class<?> type, String prefix, Class<?> handler) {
        StringBuilder builder = new StringBuilder();
        builder.append("#{").append(prefix).append(property).append(",javaType=").append(type.getCanonicalName());
        if (handler != null) {
            builder.append(",typeHandler=").append(handler.getCanonicalName());
        }

        builder.append("}");
        return builder.toString();
    }
}
